/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package edu.yonsei.iwb.smarttv.querygraph;

import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.graph.Triple;
import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.rdf.model.*;
import com.hp.hpl.jena.sparql.expr.E_Regex;
import com.hp.hpl.jena.sparql.expr.E_Str;
import com.hp.hpl.jena.sparql.expr.Expr;
import com.hp.hpl.jena.sparql.expr.ExprVar;
import com.hp.hpl.jena.sparql.syntax.ElementFilter;
import com.hp.hpl.jena.sparql.syntax.ElementGroup;
import com.hp.hpl.jena.sparql.syntax.ElementTriplesBlock;
import com.hp.hpl.jena.vocabulary.OWL;
import com.hp.hpl.jena.vocabulary.RDF;
import com.hp.hpl.jena.vocabulary.RDFS;
import edu.yonsei.iwb.smarttv.gui.CurrentProcedureInfo;
import edu.yonsei.iwb.smarttv.model.*;
import edu.yonsei.iwb.smarttv.querygraph.algorithm.*;
import edu.yonsei.iwb.smarttv.result.*;
import edu.yonsei.iwb.smarttv.userpreference.UserPreferenceModel;
import edu.yonsei.iwb.smarttv.util.SMARTTVLog;
import edu.yonsei.iwb.smarttv.util.UtilMethodSet;
import java.util.*;

/**
 *
 * @author JDK
 */
public class DynamicSPARQLGenerator {

    private final Resource TARGETRESOURCE = SMARTTVModel.TARGET_CLASS_URI;
    private Resource TARGETRESOURCE_FROM_QUERY;
    private Model model;
    private Vector<MNode> nodes;
    private Hashtable<Resource, Integer> nodeInfo;
    private MatrixModel matrixModel;
    private double[][] weightInfo;
    private AStarAlgorithm asa;
    private int v_num;
    private int ctid;
    private SMARTTVModel tvModel;    
    private Vector<ConceptualGraph> conceptualGraphSet;
    private Vector<ConceptualGraph> conceptualTreeSet;
    private UserPreferenceModel upModel;

    public DynamicSPARQLGenerator(SMARTTVModel model) {
        this.tvModel = model;
        this.model = model.getJenaModel();
        this.matrixModel = model.getMatrixModel();
        this.nodes = this.matrixModel.getNodeSet();
        this.nodeInfo = this.matrixModel.getNodeIDTable();
        this.weightInfo = this.matrixModel.getWeightInfo();
        this.asa = new AStarAlgorithm(model);
        this.conceptualGraphSet = new Vector<ConceptualGraph>();
        this.conceptualTreeSet = new Vector<ConceptualGraph>();
    }
    
    public DynamicSPARQLGenerator(SMARTTVModel model, UserPreferenceModel upmodel) { //constructor for gui
        this.tvModel = model;
        this.upModel = upmodel;
        this.model = model.getJenaModel();
        this.matrixModel = model.getMatrixModel();
        this.nodes = this.matrixModel.getNodeSet();
        this.nodeInfo = this.matrixModel.getNodeIDTable();
        this.weightInfo = this.matrixModel.getWeightInfo();
        this.asa = new AStarAlgorithm(model);
        this.conceptualGraphSet = new Vector<ConceptualGraph>();
        this.conceptualTreeSet = new Vector<ConceptualGraph>();
    }

    /**
     * 사용자 질의문의 개체 식별 결과를 바탕으로 동적으로 SPARQL을 생성함
     *
     * @param ner 개체 식별 결과
     * @return SPARQL Query 집합
     */
    public ShortestPathVectorCandidateSet dynamicSPARQLGenerator(NERResults ner) {
        this.initializeNodeNumber();
        ctid = 0;
                
        ShortestPathVectorCandidateSet candidateSP = new ShortestPathVectorCandidateSet();
        QueryCombinationSet combinationQuerySet = this.makeQueryCombinationSet(ner);
        
        if(CurrentProcedureInfo.USER_ASSIGNED){
            combinationQuerySet.orderByScore(this.upModel.getWeightModel());            
        }

        for (int i = 0; i < combinationQuerySet.size(); i++) {            
            QueryCombination qc = combinationQuerySet.get(i);
            this.checkMediaClassExist(qc);
            LinkedHashMap<ResourceNode, Vector<ConceptArc>> conceptArcTable = this.generateConceptArcSetOfQuery(qc);
            Vector<ConceptualGraph> conceptualTrees = this.generateConceptualTree(conceptArcTable, qc); // Conceptual Tree generate

            if (qc.size()==1) {
                // 쿼리가 키워드 하나로만 이루어져 있을경우
                ShortestPathSet sps = new ShortestPathSet();
                ResourceNode rn = qc.firstElement();
                ConceptArc ca = new ConceptArc();
                ShortestPath sp = new ShortestPath();
                Vector<Resource> classname = new Vector<Resource>();
                Vector<Node> variable = new Vector<Node>();
                classname.add(rn.getNode());
                variable.add(rn.getVariableNode());
                VariableMap vm = new VariableMap(classname, variable);
                sp.setVariableMap(vm);
                sps.put(ca, sp);
                sps.setTargetURI(rn.getNode());
                sps.setScore(this.calculateShortestPathVectorScore(sps));
                candidateSP.add(sps);
            } else {
                ConceptualGraph allConceptArcSet = this.getUnduplicatedConceptArcSet(conceptualTrees);
                Vector<ShortestPathSet> spsSet = this.asa.getShortestPathVector(allConceptArcSet);
                int sptIndexID = 0;
                
                for (int j = 0; j < spsSet.size(); j++) {
                    ShortestPathSet spv = spsSet.get(j);
                    spv.setVariablesOfShortestPath(this.v_num);
                    this.v_num = spv.getVariableSeq();
                    if (!spv.isEmpty()) {                        
                        for (int k = 0; k < conceptualTrees.size(); k++) {
                            ConceptualGraph st = conceptualTrees.get(k);                            
                            ShortestPathSet subspv = spv.getConceptArcMatchedSPV(st);
                            if (subspv.size() == st.size()) {                                
                                //target resource 입력
                                subspv.setTargetURI(this.TARGETRESOURCE_FROM_QUERY);
                                subspv.setScore(this.calculateShortestPathVectorScore(subspv));
                                subspv.setSPID(st.getCGID() + "-" + "SP" + sptIndexID);
                                candidateSP.add(subspv);
                                sptIndexID++;
                            }
                        }
                    }
                }
            }
        }
        candidateSP.orderByScore();
//        candidateSP.printTemporalResult();
//        SMARTTVLog.log(SMARTTVLog.DETAIL, "Shortest Path Vector of All Candidate Spanning Tree: \n" + candidateSP.toString());
        return candidateSP;
    }
    
    /**
     * 사용자 질의문의 개체 식별 결과를 바탕으로 동적으로 SPARQL을 생성함
     *
     * @param qc 개체 식별 결과     
     * @return SPARQL Query 집합
     */
    public ShortestPathVectorCandidateSet dynamicSPARQLGenerator(QueryCombination qc) {
        SMARTTVLog.log(SMARTTVLog.DETAIL, "::Find query graph of " + qc.toString()+"\n");        
        ctid = 0;
        
        ShortestPathVectorCandidateSet candidateSP = new ShortestPathVectorCandidateSet();
            
        this.checkMediaClassExist(qc);
        LinkedHashMap<ResourceNode, Vector<ConceptArc>> conceptArcTable = this.generateConceptArcSetOfQuery(qc);
        Vector<ConceptualGraph> conceptualTrees = this.generateConceptualTree(conceptArcTable, qc); // Conceptual Tree generate

        if (qc.size()==1) {
            // 쿼리가 키워드 하나로만 이루어져 있을경우
            ShortestPathSet sps = new ShortestPathSet();
            ResourceNode rn = qc.firstElement();
            ConceptArc ca = new ConceptArc();
            ShortestPath sp = new ShortestPath();
            Vector<Resource> classname = new Vector<Resource>();
            Vector<Node> variable = new Vector<Node>();
            classname.add(rn.getNode());
            variable.add(rn.getVariableNode());
            VariableMap vm = new VariableMap(classname, variable);
            sp.setVariableMap(vm);
            sps.put(ca, sp);
            sps.setTargetURI(rn.getNode());
            sps.setScore(this.calculateShortestPathVectorScore(sps));
            candidateSP.add(sps);
        } else {
            ConceptualGraph allConceptArcSet = this.getUnduplicatedConceptArcSet(conceptualTrees);
            Vector<ShortestPathSet> spsSet = this.asa.getShortestPathVector(allConceptArcSet);
            int sptIndexID = 0;

            for (int j = 0; j < spsSet.size(); j++) {
                ShortestPathSet spv = spsSet.get(j);
                spv.setVariablesOfShortestPath(this.v_num);
                this.v_num = spv.getVariableSeq();
                if (!spv.isEmpty()) {                        
                    for (int k = 0; k < conceptualTrees.size(); k++) {
                        ConceptualGraph st = conceptualTrees.get(k);                            
                        ShortestPathSet subspv = spv.getConceptArcMatchedSPV(st);
                        if (subspv.size() == st.size()) {                                
                            //target resource 입력
                            subspv.setTargetURI(this.TARGETRESOURCE_FROM_QUERY);
                            subspv.setScore(this.calculateShortestPathVectorScore(subspv));
                            subspv.setSPID(st.getCGID() + "-" + "SP" + sptIndexID);
                            candidateSP.add(subspv);
                            sptIndexID++;
                        }
                    }
                }
            }
        }

        candidateSP.orderByScore();
//        candidateSP.printTemporalResult();
//        SMARTTVLog.log(SMARTTVLog.DETAIL, "Shortest Path Vector of All Candidate Spanning Tree: \n" + candidateSP.toString());
        return candidateSP;
    }
   
    private ConceptualGraph getUnduplicatedConceptArcSet(Vector<ConceptualGraph> conceptualTreeSet){
        ConceptualGraph cg = new ConceptualGraph();
        for (int i = 0; i < conceptualTreeSet.size(); i++) {
            ConceptualGraph ct = conceptualTreeSet.get(i);
            for (int j = 0; j < ct.size(); j++) {
                ConceptArc ca = ct.get(j);
                if(!cg.contains(ca)){
                    cg.add(ca);
                }
            }
        }
           
        return cg;
    }

    private LinkedHashMap<ResourceNode, Vector<ConceptArc>> generateConceptArcSetOfQuery(QueryCombination qc) {

        LinkedHashMap<ResourceNode, Vector<ConceptArc>> result = new LinkedHashMap<ResourceNode, Vector<ConceptArc>>();

        for (int i = 0; i < qc.size() - 1; i++) {
            Vector<ConceptArc> arcset = new Vector<ConceptArc>();
            ResourceNode source = qc.get(i);
            for (int j = i + 1; j < qc.size(); j++) {
                
                ResourceNode target = qc.get(j);
                ConceptArc sub_cg;
                if (qc.get(i).getType() == NodeType.LITERAL_TYPE) {
                    if (qc.get(j).getType() != NodeType.PROPERTY_TYPE && qc.get(j).getType() != NodeType.LITERAL_TYPE && qc.get(j).getType() != NodeType.INSTANCE_TYPE) {
                        sub_cg = new ConceptArc(source, target, ArcType.LITERAL_TYPE);
                        arcset.add(sub_cg);
//                        break; // Literal value의 바로 옆에 있는 애만 아크로 생성
                    }
                } else if ((qc.get(i).getType() == NodeType.CLASS_TYPE || qc.get(i).getType() == NodeType.INSTANCE_TYPE) && (qc.get(j).getType() == NodeType.CLASS_TYPE || qc.get(j).getType() == NodeType.INSTANCE_TYPE)) {
                    if (qc.get(i).getType() == NodeType.INSTANCE_TYPE && qc.get(j).getType() == NodeType.INSTANCE_TYPE) {
                        continue;
                    } else {
                        //맨 끝이 그냥 프로퍼티 인 경우??
                        Vector<Integer> propertyIndex = new Vector<Integer>();

                        for (int k = i + 1; k < j; k++) {
                            if (qc.get(k).getType() == NodeType.PROPERTY_TYPE) {
                                propertyIndex.add(k);
                            }
                        }
                        
                        if (propertyIndex.isEmpty()) {
                            sub_cg = new ConceptArc(source, target);
                            arcset.add(sub_cg);
                        } else if (propertyIndex.size() == 1) {

                            Resource property = qc.get(propertyIndex.firstElement()).getNode();
                            sub_cg = new ConceptArc(source, target, ResourceFactory.createProperty(property.getURI()));
                            arcset.add(sub_cg);
                        } else {
                            Vector<Integer> copyOfPropertyIndex = new Vector<Integer>();
                            Vector<Integer> usedCoProperty = new Vector<Integer>();
                            
                            for (int k = 0; k < propertyIndex.size(); k++) {
                                copyOfPropertyIndex.add(propertyIndex.get(k));
                            }                            
                            
                            boolean flush = false;
                            for (int k = 0; k < propertyIndex.size() - 1; k++) {
                                int prev = propertyIndex.get(k);
                                int next = propertyIndex.get(k+1);
                                if ((next - prev) == 1) {
                                    //두 프로퍼티가 붙어있으면 컨셉아크 하나에 프로퍼티 제약 둘다 추가
                                    flush = true;
                                    if (!usedCoProperty.contains(prev)) {
                                        usedCoProperty.add(prev);
                                        copyOfPropertyIndex.removeElement(prev);
                                    }
                                    if (!usedCoProperty.contains(next)) {
                                        usedCoProperty.add(next);
                                        copyOfPropertyIndex.removeElement(next);
                                    }
                                } else {
                                    if (flush) {
                                        ConceptArc ca = new ConceptArc(source, target);
                                        for (int l = 0; l < usedCoProperty.size(); l++) {
                                            Resource p = qc.get(usedCoProperty.get(l)).getNode();
                                            ca.addProperty(ResourceFactory.createProperty(p.getURI()));
                                        }
                                        arcset.add(ca);
                                        usedCoProperty.clear();
                                        flush = false;
                                    }
                                }
                            }

                            if (flush) {
                                ConceptArc ca = new ConceptArc(source, target);
                                for (int l = 0; l < usedCoProperty.size(); l++) {
                                    Resource p = qc.get(usedCoProperty.get(l)).getNode();
                                    ca.addProperty(ResourceFactory.createProperty(p.getURI()));
                                }
                                arcset.add(ca);
                                usedCoProperty.clear();
                            }

                            for (int t = 0; t < copyOfPropertyIndex.size(); t++) {
                                Resource p = qc.get(copyOfPropertyIndex.get(t)).getNode();
                                ConceptArc ca = new ConceptArc(source, target, ResourceFactory.createProperty(p.getURI()));
                                arcset.add(ca);
                            }
                        }                        
                    }
                }
            }
            
            if(!arcset.isEmpty()){
                result.put(source, arcset);                
            }

        }

        return result;
    }

    private Vector<ConceptualGraph> generateConceptualTree(LinkedHashMap<ResourceNode, Vector<ConceptArc>> catable, QueryCombination qc) {
        Vector<ConceptualGraph> cndResult = new Vector<ConceptualGraph>();
        
        int pnum =  qc.getSizeOfPropertyType();
        Set<ResourceNode> keyset = catable.keySet();
        Iterator<ResourceNode> it = keyset.iterator();
        while (it.hasNext()) {
            ResourceNode rn = it.next();
            Vector<ConceptArc> caset = catable.get(rn);
            this.updateConceptualTree(cndResult, caset, qc);
        }        
        
        Vector<ConceptualGraph> finalresult = new Vector<ConceptualGraph>();
        for (int i = 0; i < cndResult.size(); i++) {
            ConceptualGraph candcg = cndResult.get(i);
            if(candcg.getAllProperties().size() == pnum){
                ConceptualGraph finalCASet = new ConceptualGraph();
                for (int j = 0; j < candcg.size(); j++) {
                    ConceptArc candca = candcg.get(j);
                    Vector<Property> pset = candca.getProperty();
                    if (pset.size() > 1) {
                        for (int k = 0; k < pset.size(); k++) {
                            Property p = pset.get(k);
                            ConceptArc separatedCA = new ConceptArc(candca.getHead(), candca.getTail(), p);
                            finalCASet.add(separatedCA);
                        }
                    }else{
                        finalCASet.add(candca);
                    }
                }
                
                finalCASet.setCGID("CT"+ctid);
                finalresult.add(finalCASet);
                this.conceptualTreeSet.add(finalCASet);
                ctid++;
                
            }
        }

        return finalresult;
    }

    private void updateConceptualTree(Vector<ConceptualGraph> oldcgSet, Vector<ConceptArc> tempcaSet, QueryCombination qc) {
        if (oldcgSet.isEmpty()) {
            for (int i = 0; i < tempcaSet.size(); i++) {
                ConceptualGraph cg = new ConceptualGraph();
                cg.add(tempcaSet.get(i));
                oldcgSet.add(cg);
            }
        } else {
            Vector<ConceptualGraph> newResultSet = new Vector<ConceptualGraph>();
            for (int i = 0; i < oldcgSet.size(); i++) {
                ConceptualGraph oldcg = oldcgSet.get(i);
                for (int j = 0; j < tempcaSet.size(); j++) {
                    ConceptArc tmpca = tempcaSet.get(j);
                    if (oldcg.checkProperConceptArc(tmpca, qc)) {
                        ConceptualGraph newcg = new ConceptualGraph();
                        newcg.updateConceptualTree(oldcg);
                        newcg.add(tmpca);
                        newResultSet.add(newcg);
                    } else {
                        continue;
                    }
                }
            }

            oldcgSet.clear();
            oldcgSet.addAll(newResultSet);
        }
    }

    private void checkMediaClassExist(QueryCombination qc) {
        for (int j = 0; j < qc.size(); j++) {
            ResourceNode rn = qc.get(j);
            Resource r = qc.get(j).getNode();
            StmtIterator stmt = this.model.listStatements(rn.getNode(), RDFS.subClassOf, TARGETRESOURCE);
            if (stmt.hasNext() || rn.getNode().equals(this.TARGETRESOURCE)) {
                if (rn.getType() == NodeType.CLASS_TYPE) {
                    this.TARGETRESOURCE_FROM_QUERY = r;
                    return;
                }
            }
        }
        ResourceNode media = new ResourceNode(nodes.get(nodeInfo.get(TARGETRESOURCE)));
        media.setVariableNode(Node.createVariable(UtilMethodSet.getVariableCharacter(this.v_num++)));
        qc.add(media);
        this.TARGETRESOURCE_FROM_QUERY = this.TARGETRESOURCE;
    }

    
    /**
     * 
     * @param qc
     * @return qc의 마지막 노드에 클래스 타입의 리소스가 존재하는지 확인
     */
    private boolean checkTargetClassExist(QueryCombination qc) {

        ResourceNode rn = qc.lastElement();
        Resource r = rn.getNode();

        if (rn.getType() == NodeType.CLASS_TYPE) {
//            SMARTTVLog.log(SMARTTVLog.DETAIL, "Target Resource is : " + r.getLocalName()+"\n");
            this.TARGETRESOURCE_FROM_QUERY = r;
//            spv.setTargetURI(r);
            return true;
        } else {
            if (TARGETRESOURCE == null) {
//                SMARTTVLog.log(SMARTTVLog.DETAIL, "Predefined Target resource is not exist\n");
                return false;
            } else {
                ResourceNode targetClass = new ResourceNode(nodes.get(nodeInfo.get(TARGETRESOURCE)));
                targetClass.setVariableNode(Node.createVariable(UtilMethodSet.getVariableCharacter(this.v_num++)));
                qc.add(targetClass);
//                spv.setTargetURI(TARGETRESOURCE);
                this.TARGETRESOURCE_FROM_QUERY = this.TARGETRESOURCE;
//                SMARTTVLog.log(SMARTTVLog.DETAIL, "Target Resource is : " + TARGET_RESOURCE.getLocalName()+"\n");
                return true;
            }
        }
    }

    public QueryCombinationSet makeQueryCombinationSet(NERResults ner) {
        this.initializeNodeNumber();
        QueryCombinationSet qcs = new QueryCombinationSet();
        Iterator<String> keyIterator = ner.getNamedEntityResults().getKeywordsSet().iterator();
        while (keyIterator.hasNext()) {
            boolean isLiteral = true;
            String key = keyIterator.next();
            Vector<ResourceNode> types = new Vector<ResourceNode>();
            NamedEntitySet nes = ner.getNamedEntitySetByString(key);
            for (int i = 0; i < nes.size(); i++) {
                NamedEntity ne = nes.get(i);
                ResultTypeInfo rti = ne.getResultTypes();
                for (int j = 0; j < rti.size(); j++) {
                    ResultNode resultNode = rti.get(j);
                    ResourceNode r = null;
                    if (resultNode.equals(OWL.ObjectProperty) || resultNode.equals(OWL.DatatypeProperty)) {
                        // 맵핑 결과가 Property
                        r = new ResourceNode(ne.getNamedEntityNode().getResultNode(), NodeType.PROPERTY_TYPE);

                    } else {
                        if (resultNode.equals(OWL.Class) || resultNode.equals(RDFS.Class)) {
                            // 맵핑 결과가 Class                            
                            r = new ResourceNode(nodes.get(nodeInfo.get(ne.getNamedEntityNode().getResultNode())));
                            r.setVariableNode(Node.createVariable(UtilMethodSet.getVariableCharacter(this.v_num++)));
                        } else if (resultNode.getLabel().equals("LiteralValue")) {
                            //맵핑 결과가 Literal
                            if (isLiteral) {
                                r = new ResourceNode(ResourceFactory.createResource(key), NodeType.LITERAL_TYPE);
                                r.setVariableNode(Node.createVariable(UtilMethodSet.getVariableCharacter(this.v_num++)));
                                isLiteral = false;
                            }
                        } else {
                            // 맵핑 결과가 Instance
                            r = new ResourceNode(nodes.get(nodeInfo.get(resultNode.getResultNode())), ne.getNamedEntityNode().getResultNode());
                        }
                    }
                    if (!types.contains(r) && r != null) {
                        types.add(r);
                    }
                }
            }
            qcs = this.makeCombination(qcs, types);
        }
        SMARTTVLog.log(SMARTTVLog.DETAIL, "Query Combination of Best NER Result: \n" + qcs.toString());
        return qcs;
    }

    private QueryCombinationSet makeCombination(QueryCombinationSet qcs, Vector<ResourceNode> types) {
        QueryCombinationSet result = new QueryCombinationSet();
        if (qcs.size() == 0) {
            for (int j = 0; j < types.size(); j++) {
                QueryCombination qc = new QueryCombination();
                ResourceNode rn = types.get(j);
                qc.add(rn);
                result.add(qc);
            }
        } else if (types.isEmpty()) {
            return qcs;
        } else {
            for (int i = 0; i < qcs.size(); i++) {
                QueryCombination qc = qcs.get(i);
                for (int j = 0; j < types.size(); j++) {
                    QueryCombination newQc = new QueryCombination(qc);
                    ResourceNode rn = types.get(j);
                    newQc.add(rn);
                    result.add(newQc);
                }
            }
        }
        return result;
    }

    public Query generateSPARQL(ShortestPathSet spv) {
        Query query = new Query();
        Vector<Triple> typeTriple = new Vector<Triple>();
        Vector<Triple> generalTriple = new Vector<Triple>();

        ElementTriplesBlock etb = new ElementTriplesBlock();
        ElementFilter filter;
        ElementGroup eg = new ElementGroup();
        Vector<Node> typeDefinedNodeList = new Vector();
        Enumeration<ConceptArc> e = spv.keys();
        while (e.hasMoreElements()) {
            ConceptArc ca = e.nextElement();
            ShortestPath sp = spv.get(ca);
            VariableMap variableMap = sp.getVariableMap();
//            SMARTTVLog.log(SMARTTVLog.DETAIL, "Variable Mapping Result:\n" + variableMap.toString());
            
            //subClassOf 제거
            Hashtable<Node, Node> nodetable = this.getReplaceableNodeTable(sp);
            
            for (int i = 0; i < sp.size(); i++) {
                MArc arc = sp.get(i);
                Resource head = arc.getHeadNode().getNode();
                Resource tail = arc.getTailNode().getNode();
                Property property = arc.getProperty();

                Node[] nodeSet = variableMap.getVarMapByIndex().get(i);
                Node headVar = nodeSet[0];
                Node tailVar = nodeSet[1];
                                
                // variable type define
                if (!typeDefinedNodeList.contains(headVar) && head.toString().startsWith("http")) {
                    if (headVar.isVariable()) {
                        if(!property.equals(RDFS.subClassOf)){
                            typeTriple.add(new Triple(headVar, RDF.type.asNode(), head.asNode()));
                            typeDefinedNodeList.add(headVar);                            
                        }                        
                    }
                }

                if (i == sp.size() - 1) {
                    if (!typeDefinedNodeList.contains(tailVar) && tail.toString().startsWith("http")) {
                        if (tailVar.isVariable()) {
                            typeTriple.add(new Triple(tailVar, RDF.type.asNode(), tail.asNode()));
                            typeDefinedNodeList.add(tailVar);
                        }
                    }
                }

                if (ca.getArcType() == ArcType.LITERAL_TYPE) {
                    // Literal이 입력됐을 경우 처리
                    Resource prev = ca.getHead().getNode();
                    Node[] nodeSet2 = variableMap.getVarMapByIndex().get(0);

                    Node l = nodeSet2[0];
                    Node r = nodeSet2[1];
                    //                generalTriple.add(new Triple(r, Node.createVariable(UtilMethodSet.getVariableCharacter(this.v_num++)), l));
                    etb.addTriple(new Triple(r, Node.createVariable(UtilMethodSet.getVariableCharacter(this.v_num++)), l));
                    Expr e1 = new E_Regex(new E_Str(new ExprVar(l)), prev.toString(), "i");
                    filter = new ElementFilter(e1);
                    eg.addElement(filter);
                } else {
                    // variable relation define
                    Node propertyVar = arc.getProperty().asNode();
                        
                    if(!property.equals(RDFS.subClassOf)){
                        if (arc.getDirection()) {
                            if(nodetable.containsKey(tailVar)){                                
                                generalTriple.add(new Triple(headVar, propertyVar, nodetable.get(tailVar)));                                
                            }else{
                                generalTriple.add(new Triple(headVar, propertyVar, tailVar));                                
                            }                            
                        } else {                            
                            if(nodetable.containsKey(tailVar)){                            
                                generalTriple.add(new Triple(nodetable.get(tailVar), propertyVar, headVar));                                
                            }else{
                                generalTriple.add(new Triple(tailVar, propertyVar, headVar));                                
                            }                            
                        }
                    }                    
                }
            }
        }

        Vector<Node> select = spv.getAllVariablesByClass(spv.getTargetURI());
        for (int i = 0; i < select.size(); i++) {
            Node selectVar = select.get(i);
            if (selectVar.isVariable()) {
                query.setQuerySelectType();
                query.addResultVar(selectVar);
                break;
            }
        }

//        this.orderByResourceWeight(generalTriple);
        for (int i = 0; i < generalTriple.size(); i++) {
            etb.addTriple(generalTriple.get(i));
        }

//        this.orderByResourceWeight(typeTriple);
        for (int i = 0; i < typeTriple.size(); i++) {
            etb.addTriple(typeTriple.get(i));
        }

        eg.addElement(etb);
        query.setQueryPattern(eg);
        SMARTTVLog.log(SMARTTVLog.DETAIL, "Generated SPARQL:\n" + query.serialize());
        return query;
    }
    
    private Hashtable<Node, Node> getReplaceableNodeTable(ShortestPath sp){
        VariableMap variableMap = sp.getVariableMap();
        Hashtable<Node, Node> nodetable = new Hashtable<Node, Node>();
        for (int i = 0; i < sp.size(); i++) {
            MArc arc = sp.get(i);
            Node[] nodeSet = variableMap.getVarMapByIndex().get(i);
            Node headVar = nodeSet[0];
            Node tailVar = nodeSet[1];            
            if(arc.getProperty().equals(RDFS.subClassOf)){                
                nodetable.put(headVar, tailVar);                
            }
        }        
        return nodetable;
    }

    private void orderByResourceWeight(Vector<Triple> tripleSet) {
        double[] d = new double[tripleSet.size()];
        Triple[] t = new Triple[tripleSet.size()];
        Vector<Triple> tempTriple = new Vector<Triple>();
        int index = 0;
        for (int i = 0; i < tripleSet.size(); i++) {
            if (!tripleSet.get(i).getPredicate().getURI().equals(RDF.type.toString())) {
                d[index] = tvModel.getWeightModel().getWeight(this.model.createResource(tripleSet.get(i).getPredicate().getURI()));
            } else {
                d[index] = tvModel.getWeightModel().getWeight(tripleSet.get(i));
            }
            t[index] = tripleSet.get(i);
            index++;
        }
        for (int i = 0; i < d.length - 1; i++) {
            double temp;
            Triple tempv;
            for (int j = 1; j < d.length; j++) {
                if (d[j - 1] < d[j]) {
                    temp = d[j - 1];
                    d[j - 1] = d[j];
                    d[j] = temp;

                    tempv = t[j - 1];
                    t[j - 1] = t[j];
                    t[j] = tempv;
                }
            }
        }

        tripleSet.clear();
        for (int i = 0; i < t.length; i++) {
            Triple tVector = t[i];
            double weight = d[i];
            if (weight == 1) {
                tempTriple.add(tVector);
            } else {
                tripleSet.add(tVector);
            }
        }
        tripleSet.addAll(tempTriple);

    }

    /**
     * ShortestPath를 이루는 MArc의 Weight sum을 Score로 반환
     *
     * @param sp ShortestPath
     * @return Sum of MArc weight
     */
    private double calculateShortestPathScore(ShortestPath sp) {
        double score = 0;
        for (int j = 0; j < sp.size(); j++) {
            double marcWeight = sp.get(j).getWeight();
            score += score + marcWeight;
        }
        return score;
    }

    /**
     * ShortestPathVector의 Score 계산
     *
     * @param spv
     * @return
     */
    private double calculateShortestPathVectorScore(ShortestPathSet spv) {
        double score = 0;
        Enumeration<ConceptArc> e = spv.keys();
        while (e.hasMoreElements()) {
            ConceptArc ca = e.nextElement();
            ShortestPath sp = spv.get(ca);
            score += this.calculateShortestPathScore(sp);
        }
        return score;
    }

    private void initializeNodeNumber() {
        this.v_num = 0;
    }

    public Vector<ConceptualGraph> getConceptualGraphSet() {
        return this.conceptualGraphSet;
    }

    public Vector<ConceptualGraph> getConceptualTreeSet() {
        return this.conceptualTreeSet;
    }
}
